/**
 *    Copyright (C) 2008, 2009  Victor Smirnov <aist11@gmail.com>
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License along
 *    with this program; if not, write to the Free Software Foundation, Inc.,
 *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *    See LICENSE.exceptions in the root folder of this project for possible
 *    exceptions
 *
 */

#ifndef _CUBEFS_CORE_BTREE_MODEL_CHECKS_HPP
#define	_CUBEFS_CORE_BTREE_MODEL_CHECKS_HPP

#include <iostream>

#include <cubefs/core/container/container.hpp>
#include <cubefs/core/btree/pages/pages.hpp>
#include <cubefs/api/models/logs.hpp>

#define CUBEFS_SOURCE "cubefs/core/btree/model/checks.hpp:" CUBEFS_AT

namespace cubefs    {
namespace ctr       {

using namespace cubefs::btree;

CUBEFS_MODEL_PART_BEGIN(ChecksName)
private:
    
public:

    typedef typename Base::Types                                                Types;
    typedef typename Base::PageMgr                                              PageMgr;

    typedef typename PageMgr::Page                                              Page;
    typedef typename Page::ID                                                   ID;

    typedef typename Types::NodeBase                                            NodeBase;
    typedef typename Types::Counters                                            Counters;
    typedef typename Base::Iterator                                             Iterator;

    typedef typename Types::Pages::NodeDispatcher                               NodeDispatcher;
    typedef typename Types::Pages::RootDispatcher                               RootDispatcher;
    typedef typename Types::Pages::LeafDispatcher                               LeafDispatcher;
    typedef typename Types::Pages::NonLeafDispatcher                            NonLeafDispatcher;
    typedef typename Types::Pages::NonRootDispatcher                            NonRootDispatcher;

    typedef typename Types::Pages::Node2RootMap                                 Node2RootMap;
    typedef typename Types::Pages::Root2NodeMap                                 Root2NodeMap;

    typedef typename Base::Metadata                                             Metadata;

    typedef typename Base::Key                                                  Key;
    typedef typename Base::Value                                                Value;

    static const Int Indexes                                                    = Types::Indexes;


//PROTECETED API:

//PUBLIC API:
    bool CheckTree() {
        NodeBase* root = me_.GetRoot();
        if (root != NULL)
        {
            bool errors = false;
            me_.check_node_tree(root, errors);
            return me_.check_keys() || errors;
        }
        else {
            return false;
        }
    }

//PRIVATE API:
    void check_node_tree(NodeBase* node, bool &errors) {

        errors = me_.check_node_content(node) || errors;

        Int children = me_.GetChildrenCount(node);

        if (node->is_leaf())
        {
            if (node->counters().page_count() != 1) {
                errors = true;
                CUBEFS_ERROR(me_, "counters.page_count != 1 for leaf node", node->id(), node->counters().page_count());
            }

            for (Int c = 0; c < children; c++)
            {
                errors = me_.check_leaf_value(node, c) || errors;
            }
        }
        else {
            Counters cnt;
            for (Int c = 0; c < children; c++)
            {
                NodeBase* child = me_.GetChild(node, c);
                cnt += child->counters();
                
                me_.check_node_tree(child, errors);

                if (!(child->parent_id() == node->id()))
                {
                    errors = true;
                    CUBEFS_ERROR(me_, "child.parent_id != parent.id", child->id(), child->parent_id(), node->id());
                }

                if (child->parent_idx() != c)
                {
                    errors = true;
                    CUBEFS_ERROR(me_, "child.parent_idx != parent.child.idx", child->id(), child->parent_idx(), c);
                }
            }

            cnt.page_count()++;

            if (cnt != node->counters()) {
                errors = true;
                CUBEFS_ERROR(me_, "node.counters doesn't match childrens.counters", node->id(), node->counters(), cnt);
            }
        }
    }

    bool check_leaf_value(NodeBase* leaf, Int idx) {
        return false;
    }

    template <typename Node>
    bool CheckNodeContent(Node *node) {
        return false;
    }

    template <typename Node1, typename Node2>
    bool CheckNodeWithParentContent(Node1 *node, Node2 *parent) {
        bool errors = false;
        for (Int c = 0; c < Indexes; c++) {
            if (node->map().max_key(c) != parent->map().key(c, node->parent_idx())) {
                CUBEFS_TRACE(me_, "Invalid parent-child nodes chain", c, node->map().max_key(c), parent->map().key(c, node->parent_idx()), "for", node->id(), parent->id(), node->parent_idx());
                errors = true;
            }
        }
        return errors;
    }

    struct CheckNodeContentFn1 {
        MyType& map_;
        bool rtn_;
        CheckNodeContentFn1(MyType& map): map_(map) {}

        template <typename T>
        void operator()(T* node) {
            rtn_ = map_.CheckNodeContent(node);
        }
    };

    struct CheckNodeContentFn2 {
        MyType& map_;
        bool rtn_;

        CheckNodeContentFn2(MyType& map): map_(map) {}

        template <typename Node1, typename Node2>
        void operator()(Node1* node, Node2* parent) {
            rtn_ = map_.CheckNodeWithParentContent(node, parent);
        }
    };

    bool check_node_content(NodeBase* node) {
        bool errors = false;
        if (node->is_root()) {
            if (!node->parent_id().is_null()) {
                errors = true;
                CUBEFS_ERROR(me_, "node.parent_id != NULL for root node", node->id(), node->parent_id());
            }

            if (node->parent_idx() != 0) {
                errors = true;
                CUBEFS_ERROR(me_, "node.parent_id != 0 for root node", node->id(), node->parent_idx());
            }

            CheckNodeContentFn1 fn1(me_);
            RootDispatcher::Dispatch(node, fn1);
            errors = fn1.rtn_ || errors;
        }
        else {
            if (node->parent_id().is_null()) {
                errors = true;
                CUBEFS_ERROR(me_, "node.parent_id == NULL for root node", node->id(), node->parent_id());
            }

            NodeBase* parent = me_.GetParent(node);
            if (parent == NULL) {
                CUBEFS_ERROR(me_, "node", node->id(), "has no parent");
            }
            else {
                CheckNodeContentFn2 fn2(me_);
                NodeDispatcher::DoubleDispatch(node, parent, fn2);
                errors = fn2.rtn_ || errors;
            }
        }

        return errors;
    }

    bool check_keys() {
        return false;
    }

CUBEFS_MODEL_PART_END

}
}

#undef CUBEFS_SOURCE

#endif	/* _CUBEFS_CORE_BTREE_MODEL_CHECKS_HPP */
